﻿///<License>BSD-NAW-01</License>
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2009, NAW Enterprises Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// - Redistributions of source code must retain the above copyright notice, 
//   this list of conditions and the following disclaimer.
//
// - Redistributions in binary form must reproduce the above copyright notice, 
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
// 
// - Neither the name NAW Enterprises Inc. nor the names of its contributors
//   may be used to endorse or promote products derived from this software 
//   without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
// POSSIBILITY OF SUCH DAMAGE.
//
////////////////////////////////////////////////////////////////////////////////
using System;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Threading;
using System.Net;
using System.IO;
using TwitterSimple;
using System.Xml.Serialization;
using System.Windows.Media.Animation;

namespace TwitterFace
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        XmlSerializer m_Serializer;
        Thread m_FeedThread;
        Random m_Random;
        DoubleAnimationUsingKeyFrames m_FadeInAnimation;

        public MainWindow()
        {
            InitializeComponent();

            
        }

        /// <summary>
        /// Creates a display item based on the parameters.
        /// </summary>
        /// <param name="Title">The title of the item. Set to the user's screen name</param>
        /// <param name="Text">The body or content of the item, Set to the tweet text.</param>
        /// <param name="Foreground">The forground color.</param>
        /// <param name="Background">The background color.</param>
        /// <returns></returns>
        private UserControl CreateItem(string Title, string Text, Color Foreground, Color Background)
        {
            //Create the background rectangle
            Rectangle r = new Rectangle();
            r.Width = 350;
            r.Height = 150;
            //Set the corner radius. We want symmetrical rounded corners, so both x and y are the same
            r.RadiusX = 10;
            r.RadiusY = 10;
            //Do a color multiply so we get a nice background blend from the user's profile background color
            Color start = Color.Multiply(Background, 2.2f);
            Color end = Color.Multiply(Background, 0.55f);
            //Modify the alpha components after the multiply
            start.A = 224;
            end.A = 248;
            //Set the fill to be a linear gradient. Make it slightly offset for a more 3d look.
            r.Fill = new LinearGradientBrush(start, end, 88);

            //Add a drop shadow
            //r.Effect = new System.Windows.Media.Effects.DropShadowEffect() { Color = Colors.Black, BlurRadius = 20, Opacity = 0.5 };

            //Create the stroke around the rectangle.
            r.Stroke = new SolidColorBrush(Foreground);
            r.StrokeThickness = 2.0;
            
            //Create the title label
            Label titlelabel = new Label();
            titlelabel.Content = Title;
            titlelabel.HorizontalAlignment = HorizontalAlignment.Left;
            titlelabel.VerticalAlignment = VerticalAlignment.Top;
            titlelabel.FontFamily = new FontFamily("Segoe UI");
            titlelabel.FontSize = 20;
            titlelabel.FontWeight = FontWeights.Bold;
            titlelabel.Padding = new Thickness(10);
            titlelabel.Foreground = new SolidColorBrush(Foreground);

            TextBlock textblock = new TextBlock();
            textblock.Text = Text;
            textblock.HorizontalAlignment = HorizontalAlignment.Center;
            textblock.VerticalAlignment = VerticalAlignment.Center;
            textblock.FontFamily = new FontFamily("Segoe UI");
            textblock.FontSize = 14;
            textblock.Padding = new Thickness(10, 20, 10, 10); //Pad around the title label
            textblock.TextWrapping = TextWrapping.WrapWithOverflow; //Cause the text to wrap nicely.
            textblock.Foreground = titlelabel.Foreground;

            //Create a user control that is going to wrap all of our elements
            UserControl item = new UserControl();
            item.Content = new Grid(); //Create a grid inside of the user control to put all of our things into
            item.Width = 350;
            item.Height = 150;
            (item.Content as Grid).Children.Add(r); //Add items to the grid.
            (item.Content as Grid).Children.Add(titlelabel);
            (item.Content as Grid).Children.Add(textblock);

            //Hook up an event for the item to fade it in when it is loaded
            item.Loaded += new RoutedEventHandler(uc_Loaded);

            return item;
        }

        void uc_Loaded(object sender, RoutedEventArgs e)
        {
            //Perform the animation, targeting the opacity property
            (sender as UserControl).BeginAnimation(FrameworkElement.OpacityProperty, m_FadeInAnimation);
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            //Initlaize the thread
            m_FeedThread = new Thread(Thread_RunThread);
            //Initialize the XmlSerializer for deserialization of the twitter feed.
            m_Serializer = new XmlSerializer(typeof(Status));
            //Initlaize a random class for determining position of items
            m_Random = new Random();
            //Create the fade in animation for our items
            m_FadeInAnimation = new DoubleAnimationUsingKeyFrames();
            m_FadeInAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(250));
            m_FadeInAnimation.KeyFrames.Add(new SplineDoubleKeyFrame(0, KeyTime.FromPercent(0)));
            m_FadeInAnimation.KeyFrames.Add(new SplineDoubleKeyFrame(1, KeyTime.FromPercent(1)));
        }

        private void Thread_RunThread()
        {
            try
            {
                //Connect to the spritzer feed from twitter. The 'stream' API's are in alpha ans may be subject to outages
                //and changes. for more info, check out http://apiwiki.twitter.com/Streaming-API-Documentation
                HttpWebRequest req = HttpWebRequest.Create("http://stream.twitter.com/spritzer.xml") as HttpWebRequest;

                //Pull the credentials from the login fields. This must be done on the UI thread.
                this.Dispatcher.Invoke((SimpleDelegate)delegate()
                {
                    req.Credentials = new NetworkCredential(txtUsername.Text, txtPassword.Password);
                });

                //Enable keep-alives
                req.KeepAlive = true;

                //Open up the response stream. We will listen to this stream for tweets
                HttpWebResponse resp = req.GetResponse() as HttpWebResponse;

                //Create a stream reader that we will use to break up the response stream
                StreamReader sr = new StreamReader(resp.GetResponseStream());

                //Create a string builder for buffering responses
                StringBuilder sb = new StringBuilder(250);

                //While the stream has not been close
                while (!sr.EndOfStream)
                {
                    string line = sr.ReadLine();

                    if (string.IsNullOrEmpty(line)) //Check for a double return, signifying the end of a particular tweet
                    {
                        //Throw the buffer off to the main system
                        string xml = sb.ToString();

                        try //If this fails, we don't really care because it is a demo application. More robust error handling should be performed.
                        {
                            Status s = m_Serializer.Deserialize(new StringReader(xml)) as Status; //Deserialize the xl

                            if (!s.User.ProfileBackgroundColor.StartsWith("#")) //This really doesn't need to be an if statement because all twitter profile colors don't start with the # sign
                                s.User.ProfileBackgroundColor = "#" + s.User.ProfileBackgroundColor;

                            if (!s.User.ProfileTextColor.StartsWith("#")) //This really doesn't need to be an if statement because all twitter profile colors don't start with the # sign
                                s.User.ProfileTextColor = "#" + s.User.ProfileTextColor;

                            this.Dispatcher.BeginInvoke((SimpleDelegate)delegate() //Display a tweet using the ui thread in async so that we don't hold up the stream
                            {
                                try
                                {
                                    UserControl c = CreateItem( //Create a display item
                                        s.User.ScreenName, 
                                        s.Text, 
                                        (Color)ColorConverter.ConvertFromString(s.User.ProfileTextColor), //Process the HTML color tags. Some twitter users have invalid colors EG Mikeyy. Twitter, when was this allowed?
                                        (Color)ColorConverter.ConvertFromString(s.User.ProfileBackgroundColor));


                                    cnvMain.Children.Add(c); //Add the item to the canvas. Will fire the Loaded Event and begin the fade in animation.

                                    //Set its position randomly
                                    double left = m_Random.Next(0, (int)(cnvMain.ActualWidth - c.Width));
                                    double top = m_Random.Next(0, (int)(cnvMain.ActualHeight - c.Height));

                                    //Use the canvas dependency property for the left and top
                                    c.SetValue(Canvas.TopProperty, top);
                                    c.SetValue(Canvas.LeftProperty, left);

                                    //If we currently have more than 25 elements on the screen, remove the oldest one
                                    if (cnvMain.Children.Count > 100)
                                        cnvMain.Children.RemoveAt(0);
                                }
                                catch { }
                            });
                        }
                        catch (ThreadAbortException ex) { throw ex; } //Specifically catch this exception to break out of the loop and terminate the thread.
                        catch { }

                        sb.Remove(0, sb.Length); //Clear the string buffer on success
                    }
                    else //If the line is not empty, add it to the buffer
                    {
                        sb.Append(line);
                    }

                }
            }
            catch { }
        }

        private delegate void SimpleDelegate(); //Must declare empty delegate for Invoke calls

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            //Kill the thread when we want to close.
            m_FeedThread.Abort();
        }

        private void btnLogin_Click(object sender, RoutedEventArgs e)
        {
            //Start the thread when they click login.
            if (m_FeedThread.ThreadState == ThreadState.Unstarted)
                m_FeedThread.Start();
        }
    }
}
